#!/usr/bin/python
#U
# Copyright (c) 2009 Julius Volz
# See LICENSE for details.

import config, dataset, debug, output, time, complexmatch, string
from format import text, html
import SPARQLWrapper as sw
from SPARQLWrapper import JSON
from febrl import stringcmp as strcmp
import urllib2
import httplib

class LinkSpec:
  def __init__(self, id, link_type, src_ds, dst_ds, restrict, metric, filter,
               thresholds, output_opts, sync_opts):
    self.id = id
    self.link_type = link_type
    self.src_ds = src_ds
    self.dst_ds = dst_ds
    self.restrict = restrict
    self.metric = metric
    self.filter = filter
    self.thresholds = thresholds
    self.output = output_opts
    self.sync = sync_opts
    self.links = {}
    self.initialized = False

    if output_opts['format'] not in output.formats:
      raise config.Error('Invalid output format "%s"' % output_opts['format'])

  def init(self):
    # If applicable, build and use index over dest resources
    if self.restrict is not None:
      debug.log(2, 'Using index for looking up destination resources.\n')
      import index
      self.dst_index = index.Index(self.dst_ds, self.restrict['dst_indexes'])
    else:
      self.dst_index = None
      debug.log(2, 'No index, using all destination resources for comparisons.\n')
      debug.log(2, "Loading destination resources...")
      self.dst_resources = self.dst_ds.resources()
      debug.log(2, "found %s.\n" % len(self.dst_resources))

    debug.log(2, "Loading source resources...")
    self.src_resources = self.src_ds.resources()
    debug.log(2, "found %s.\n" % len(self.src_resources))

    self.initialized = True

  def run(self, sync_daemon=False):
    if not self.initialized:
      self.init()

    # Perform actual comparisons and create links
    self.compare_and_link()

    if self.filter is not None:
      self.filter_links()

    self.accept_links = []
    self.verify_links = []

    for links in self.links.values():
      for link in links:
        if link['similarity'] > self.thresholds['accept']:
          self.accept_links.append(link)
        elif link['similarity'] > self.thresholds['verify']:
          self.verify_links.append(link)

    self.write_links()

    if sync_daemon:
      from sync.source import sync
      sync.add_links(self.id, self.accept_links, self.link_type)


  def validate_link(self, src_resource, dst_resource):
    if not self.initialized:
      self.init()

    return self.compare_pair(src_resource, dst_resource) > self.thresholds['accept']


  def compare_and_link(self):
    debug.log(2, "Linking...\n")

    if self.dst_index is not None:
      src_restrict_path = self.restrict['src']

    num_comparisons = 0
    for src_resource in self.src_resources:
      if self.dst_index is not None:
        dst_resources = self.dst_index.search(src_restrict_path,
                                              src_resource,
                                              self.src_ds,
                                              self.restrict['limit'])
      else:
        dst_resources = self.dst_resources

      for dst_resource in dst_resources:
        if config.verbosity >= 4:
          trace = {}
        else:
          trace = None
        similarity = self.compare_pair(src_resource, dst_resource, trace)

	#do the complex matching
        if similarity > self.thresholds['verify']:
	  if config.complexquery.values() != []:
            if similarity != 1:
              complex = None
	      while(complex == None):
	        complex = self.complex_matching(src_resource, dst_resource, similarity)
	        print complex
                if(complex == True):
                  self.add_link(src_resource, dst_resource, 1)
	  else:
            if similarity != 1:
	      self.add_link(src_resource, dst_resource, similarity)

        num_comparisons += 1

        if num_comparisons % 100 == 0:
          debug.log(3,'%s %s' % (time.time(), num_comparisons))

  def complex_matching(self, src_resource, dst_resource, similarity):

    lst1 = []
    lst2 = []

    i = 1
    accept = ''
    metric = ''
    least_similar = 0

    for cq in config.complexquery.values():
    	datasource = config.datasources[cq.id]
	metric = cq.metric
	accept = float(cq.accept)
	least_similar = cq.least_similar 

        prefixes = ''
        for prefix in config.prefixes:
          prefixes += 'PREFIX %s: <%s>\n' % (prefix, config.prefixes[prefix])

	prefixes += cq.query

	if datasource.graph != None:
	  from_query = 'FROM <'+datasource.graph+'> WHERE {'
	  qq = string.replace(prefixes,'WHERE {',from_query)
	  prefixes = qq
	
	if(i == 1):
	  qq2 = string.replace(prefixes,cq.replace,'<'+dst_resource+'>')
        else:
	  qq2 = string.replace(prefixes,cq.replace,'<'+src_resource+'>')

	sparql = sw.SPARQLWrapper(datasource.endpoint_uri)
    	sparql.setQuery("""%s""" % (qq2))

        sparql.setReturnFormat(JSON)

	try:
    	  results = sparql.query().convert()
          #append co_authors from destination to a list
    	  for result in results["results"]["bindings"]:
            if(i == 1):
      	      lst1.append(result["name"]["value"])
            else:
              lst2.append(result["name"]["value"])
          i += 1
	except sw.SPARQLExceptions.QueryBadFormed:
	  raise Error('\nBad SPARQL request:\n%s' % (query))
	except (urllib2.URLError, httplib.BadStatusLine), e:
	  debug.log(1, 'Connection error during SPARQL request to "%s": %s\n' %
		        (datasource.endpoint_uri, e))
	  print 'We are going to sleep!'
	  time.sleep(5)
          return None

    print "Compare %s and %s" % (src_resource,dst_resource)

    found = 0
    foundlist1 = []
    for s in lst2:
      for q in lst1:
        if strcmp.do_stringcmp(metric.algorithm,s, q)[0] > accept:
          combi = s+'-'+q
	  if combi not in foundlist1:
              found = found + 1
	      foundlist1.append(combi)
              print 'Found %s and %s!' % (s, q)
          else:
            pass
    if found > least_similar:
      return True
    else:
      return False
        
  def add_link(self, src_resource, dst_resource, similarity):
    if src_resource not in self.links:
      self.links[src_resource] = []
    
    debug.log(6, 'ADDING LINK: <%s> %s <%s>\n' % (src_resource, self.link_type, dst_resource))
    self.links[src_resource].append({'src': src_resource,
                                     'dst': dst_resource,
                                     'similarity': similarity})

  def filter_links(self):
    debug.log(2, "Applying link limit...\n")
    limit = self.filter['limit']
    sort_func = lambda x, y: x['similarity'] < y['similarity'] and 1 or -1
    for src in self.links:
      self.links[src].sort(sort_func)
      self.links[src] = self.links[src][:limit]

    rev_links = {}
    for src in self.links:
      for link in self.links[src]:
        if link['dst'] not in rev_links:
          rev_links[link['dst']] = []
        rev_links[link['dst']].append(link)

    for dst in rev_links:
      rev_links[dst].sort(sort_func)
      rev_links[dst] = rev_links[dst][:limit]

    self.links = rev_links

  def write_links(self):
    # TODO: error handling, unspecified files, etc.
    accept_file = open(self.output['accept'], self.output['mode'])
    verify_file = open(self.output['verify'], self.output['mode'])

    output.output(self.accept_links, self.link_type, self.output['namespace'],
                  accept_file, self.output['format'])
    output.output(self.verify_links, self.link_type, self.output['namespace'],
                  verify_file, self.output['format'])

    accept_file.close()
    verify_file.close()

  def compare_pair(self, src_resource, dst_resource, trace=None):
    debug.log_indent(3, "\nComparing '%s' and '%s':\n" % (src_resource, dst_resource))
    # setup variable bindings for source and destination datasets
    bindings = {
      self.src_ds.var: [{
        'type': 'uri',
        'value': src_resource,
        'dataset': self.src_ds
      }],
      self.dst_ds.var: [{
        'type': 'uri',
        'value': dst_resource,
        'dataset': self.dst_ds
      }]
    }

    similarity = self.metric.evaluate(bindings, trace)

    if trace is not None:
      trace['value'] = similarity

      if config.verbosity >= 4:
        debug.log(4, text.format([trace]))

    debug.log_outdent(3, "Total similarity: %s\n" % similarity)
    return similarity

  def find_matches(self, src_resource):
    #var = self.src_ds.var # TODO: hack
    #self.src_ds = dataset.CrawlerDataset(src_resource, var)

    if not self.initialized:
      self.init()

    self.src_resources = self.src_ds.resources()

    # Perform actual comparisons and create links
    self.compare_and_link()

    if self.filter is not None:
      self.filter_links()

    return self.links
